#pragma once

#ifndef _Out_writes_
#define _Out_writes_(x)
#define _Out_writes_opt_(x)
#define _Out_writes_bytes_(x)
#define _Out_writes_bytes_opt_(x)
#define _Out_writes_to_(x, y)
#define _Out_writes_to_opt_(x, y)
#define _Out_writes_bytes_to_(x, y)
#define _In_reads_(x)
#define _In_reads_opt_(x)
#define _In_reads_bytes_(x)
#define _In_reads_bytes_opt_(x)
#define _Reserved_
#define _Analysis_noreturn_
#define _Outptr_
#define _Outptr_opt_
#define _Outptr_result_maybenull_
#define _Outptr_result_buffer_(x)
#define _Field_size_opt_(x)
#endif

#include <windows.h>
#include "../windbg/inc/dbgeng.h"
#define KDEXT_64BIT
#include "../windbg/inc/wdbgexts.h"
#include <exception>
#include "ByteArray.h"

#include <pshpack8.h>

/*! \mainpage WinDBG Script Extension
 *
 * \section intro_sec Introduction
 *
 * This extension allows use of scripting languages like Perl instead of embedded WinDBG script language.
 * The reasons behind it:
 *	- common scripting languages are much more powerful (Perl's regexp etc.)
 *	- scripting languages usually have an extension library with tons of ready solutions, f.e. Perl's CPAN.
 *	- scripts can be debugged (Note! Perl's internal debugger didn't work because of trouble with ReadLine.pm, but ptkdb works fine)
 * 
 * My primary goal was to get powerful and flexible tool for dump analysis.
 * Because of this some of WinDBG API features aren't available in scripts. Especially WinDBG instance creation, control, and some others.
 * All other WinDBG API interfaces (referenced in MSDN "Debug Engine Interfaces" topic) are available in scripts.
 * Special interface \ref WindbgExt provides access to these interfaces and some methods from ExtExtension.
 * 
 * In scripts the only entry point for WinDBG API is a function \ref dbg() that returns reference to \ref WindbgExt instance.
 * This is a global function prototyped as:\n
 * WindbgExt* dbg();
 * 
 * Due to nature of COM-interface description I've glued numeric versions of the same interface into the highest one to avoid tons of duplicate methods.
 * I.e. method \ref WindbgExt::ExtAdvanced() always returns reference typed as IDebugAdvanced3* although really it can be IDebugAdvanced2*.
 * Of course if you call f.e. GetSourceFileInformationWide() method on that reference it'll crash.
 * So to deal with availability of the WinDBG interfaces there are methods \ref WindbgExt::GetSupportedInterfaces, \ref WindbgExt::CheckAvailableInterfaces
 * and \ref WindbgExt::RequireInterfaces.
 * They allow to check what WinDBG interfaces are really provided by the debug engine and adjust script behavior, f.e. just finish script with error.
 *
 * Another modification of the WinDBG API is a change of parameters of some methods. The mostly it affected the methods that return some data other than integers.
 * To unify these modifications special class \ref ByteArray was introduced. It treats data as a byte array that caller can reinterpret according to its needs.
 * Here is list of changed methods - \ref modified_methods.
 *
 * \section perl_example Script example
 *
 * \code{.pl}
 * use Windbg;
 * 
 * windbg::dbg()->RequireInterfaces("IDebugAdvanced3, IDebugClient7, IDebugControl7");
 *
 * my $advanced = windbg::dbg()->ExtAdvanced();
 * my $byteArray = new windbg::ByteArray();
 * $byteArray->Resize(100);
 * printf "context before = 0x%.08X\n", $byteArray->DWORD_At(2);
 * $advanced->SetThreadContext($byteArray);
 * $advanced->GetThreadContext($byteArray);
 * printf "context after = 0x%.08X\n", $byteArray->DWORD_At(2);
 * my $ba2 = new windbg::ByteArray();
 * my $outSize = 0;
 * $ba2->Resize(10);
 * $advanced->Request(100, $byteArray, $ba2, \$outSize);
 * print "request result: ", $ba2->Str_At(0), "\n";
 * $advanced->GetSourceFileInformation(10, "test.cpp", 10, 10, $ba2);
 * print "file info: ", $ba2->Str_At(0), "\n";
 * \endcode
 *
 * \section install_sec Installation
 *
 * Installation is rather simple: download scriptext-x.x.x package and follow instructions in install.txt.
 *  
 * \page modified_methods Modified WinDBG API methods
 * 
 * List of methods whose parameters are different in scripts:
 * 
 * IDebugAdvanced
 *  - GetSourceFileInformation;
 *  - FindSourceFileAndToken;
 *  - GetSymbolInformation;
 *  - GetSystemObjectInformation;
 *  - GetSourceFileInformationWide;
 *  - FindSourceFileAndTokenWide;
 *  - GetSymbolInformationWide;
 *  
 * IDebugClient
 *  - GetRunningProcessSystemIds;
 *  - GetRunningProcessDescription;
 *  - GetOutputLinePrefix;
 *  - GetIdentity;
 *  - GetRunningProcessDescriptionWide;
 *  - GetDumpFile;
 *  - GetDumpFileWide;
 *  - GetOutputLinePrefixWide;
 *  - GetIdentityWide;
 *  - GetQuitLockString;
 *  - GetQuitLockStringWide;
 *  - SetClientContext;
 *  
 * IDebugControl
 *  - GetLogFile;
 *  - Disassemble;
 *  - GetSystemVersion;
 *  - GetProcessorTypeNames;
 *  - GetTextMacro;
 *  - GetBreakpointParameters;
 *  - GetEventFilterText;
 *  - GetEventFilterCommand;
 *  - GetSpecificFilterParameters;
 *  - SetSpecificFilterParameters;
 *  - GetSpecificFilterArgument;
 *  - GetExceptionFilterParameters;
 *  - SetExceptionFilterParameters;
 *  - GetExceptionFilterSecondCommand;
 *  - GetLastEventInformation;
 *  - GetTextReplacement;
 *  - GetExpressionSyntaxNames;
 *  - GetEventIndexDescription;
 *  - GetLogFileWide;
 *  - DisassembleWide;
 *  - GetProcessorTypeNamesWide;
 *  - GetTextMacroWide;
 *  - GetEventFilterTextWide;
 *  - GetEventFilterCommandWide;
 *  - GetSpecificFilterArgumentWide;
 *  - GetExceptionFilterSecondCommandWide;
 *  - GetLastEventInformationWide;
 *  - GetTextReplacementWide;
 *  - GetExpressionSyntaxNamesWide;
 *  - GetEventIndexDescriptionWide;
 *  - GetLogFile2;
 *  - GetLogFile2Wide;
 *  - GetSystemVersionString;
 *  - GetSystemVersionStringWide;
 *  - GetContextStackTrace;
 *  - OutputContextStackTrace;
 *  - GetStoredEventInformation;
 *  - GetManagedStatus;
 *  - GetManagedStatusWide;
 *  - GetStackTraceEx;
 *  - OutputStackTraceEx;
 *  - GetContextStackTraceEx;
 *  - OutputContextStackTraceEx;
 *  
 * IDebugBreakpoint
 *  - GetCommand;
 *  - GetOffsetExpression;
 *  - GetCommandWide;
 *  - GetOffsetExpressionWide;
 *  
 * IDebugDataSpaces
 *  - ReadVirtual;
 *  - WriteVirtual;
 *  - SearchVirtual;
 *  - ReadVirtualUncached;
 *  - WriteVirtualUncached;
 *  - ReadPointersVirtual;
 *  - WritePointersVirtual;
 *  - ReadPhysical;
 *  - WritePhysical;
 *  - ReadControl;
 *  - WriteControl;
 *  - ReadIo;
 *  - WriteIo;
 *  - ReadBusData;
 *  - WriteBusData;
 *  - ReadDebuggerData;
 *  - ReadProcessorSystemData;
 *  - GetVirtualTranslationPhysicalOffsets;
 *  - ReadHandleData;
 *  - FillVirtual;
 *  - FillPhysical;
 *  - ReadTagged;
 *  - GetOffsetInformation;
 *  - SearchVirtual2;
 *  - ReadMultiByteStringVirtual;
 *  - ReadMultiByteStringVirtualWide;
 *  - ReadUnicodeStringVirtual;
 *  - ReadUnicodeStringVirtualWide;
 *  - ReadPhysical2;
 *  - WritePhysical2;
 *  
 * IDebugRegisters
 *  - GetDescription;
 *  - GetValues;
 *  - SetValues;
 *  - GetDescriptionWide;
 *  - GetPseudoDescription;
 *  - GetPseudoDescriptionWide;
 *  - GetPseudoValues;
 *  - SetPseudoValues;
 *  - GetValues2;
 *  - SetValues2;
 *  
 * IDebugSymbols
 *  - GetNameByOffset;
 *  - GetNearNameByOffset;
 *  - GetLineByOffset;
 *  - GetModuleNames;
 *  - GetModuleParameters;
 *  - GetTypeName;
 *  - ReadTypedDataVirtual;
 *  - WriteTypedDataVirtual;
 *  - ReadTypedDataPhysical;
 *  - WriteTypedDataPhysical;
 *  - GetScope;
 *  - SetScope;
 *  - GetNextSymbolMatch;
 *  - GetSymbolPath;
 *  - GetImagePath;
 *  - GetSourcePath;
 *  - GetSourcePathElement;
 *  - FindSourceFile;
 *  - GetSourceFileLineOffsets;
 *  - GetModuleVersionInformation;
 *  - GetModuleNameString;
 *  - GetConstantName;
 *  - GetFieldName;
 *  - GetNameByOffsetWide;
 *  - GetNearNameByOffsetWide;
 *  - GetLineByOffsetWide;
 *  - GetTypeNameWide;
 *  - GetNextSymbolMatchWide;
 *  - GetSymbolPathWide;
 *  - GetImagePathWide;
 *  - GetSourcePathWide;
 *  - GetSourcePathElementWide;
 *  - FindSourceFileWide;
 *  - GetSourceFileLineOffsetsWide;
 *  - GetModuleVersionInformationWide;
 *  - GetModuleNameStringWide;
 *  - GetConstantNameWide;
 *  - GetFieldNameWide;
 *  - GetFunctionEntryByOffset;
 *  - GetSymbolEntriesByOffset;
 *  - GetSymbolEntriesByName;
 *  - GetSymbolEntriesByNameWide;
 *  - GetSymbolEntryString;
 *  - GetSymbolEntryStringWide;
 *  - GetSymbolEntryOffsetRegions;
 *  - GetSourceEntriesByOffset;
 *  - GetSourceEntriesByLine;
 *  - GetSourceEntriesByLineWide;
 *  - GetSourceEntryString;
 *  - GetSourceEntryStringWide;
 *  - GetSourceEntryOffsetRegions;
 *  - GetScopeEx;
 *  - SetScopeEx;
 *  - GetNameByInlineContext;
 *  - GetNameByInlineContextWide;
 *  - GetLineByInlineContext;
 *  - GetLineByInlineContextWide;
 *  
 * IDebugSymbolGroup
 *  - GetSymbolName;
 *  - GetSymbolParameters;
 *  - GetSymbolNameWide;
 *  - GetSymbolTypeName;
 *  - GetSymbolTypeNameWide;
 *  - GetSymbolValueText;
 *  - GetSymbolValueTextWide;
 *  
 * IDebugSystemObjects
 *  - GetThreadIdsByIndex;
 *  - GetProcessIdsByIndex;
 *  - GetCurrentProcessExecutableName;
 *  - GetSystemIdsByIndex;
 *  - GetCurrentSystemServerName;
 *  - GetCurrentProcessExecutableNameWide;
 *  - GetCurrentSystemServerNameWide;
 * 
 */


//////////////////////////////////////////////////////////////////////////
// the only allowed way to retrieve WindbgExt interface
class WindbgExt;

/** \fn WindbgExt* dbg()
 * \brief Returns instance of the WindbgExt.
 * \return reference to WindbgExt instance.
 */
WindbgExt* STDMETHODCALLTYPE dbg();


//////////////////////////////////////////////////////////////////////////
// Utilities
struct NoThrowException
{
	const char* what;
};

//////////////////////////////////////////////////////////////////////////
// Output capture interface
interface DECLSPEC_NOVTABLE CaptureOutputA: public IUnknown
{
public:
	virtual void STDMETHODCALLTYPE Start(void) = 0;
	virtual void STDMETHODCALLTYPE Stop(void) = 0;
	virtual void STDMETHODCALLTYPE Delete(void) = 0;
	virtual void STDMETHODCALLTYPE Execute(_In_ PCSTR Command) = 0;
	virtual const char* STDMETHODCALLTYPE GetTextNonNull(void) = 0;
};

interface DECLSPEC_NOVTABLE CaptureOutputW: public IUnknown
{
public:
	virtual void STDMETHODCALLTYPE Start(void) = 0;
	virtual void STDMETHODCALLTYPE Stop(void) = 0;
	virtual void STDMETHODCALLTYPE Delete(void) = 0;
	virtual void STDMETHODCALLTYPE Execute(_In_ PCSTR Command) = 0;
	virtual const wchar_t* STDMETHODCALLTYPE GetTextNonNull(void) = 0;
};

//////////////////////////////////////////////////////////////////////////
/// Interface to WinDbg engine.
/** This is a wrapper for ExtExtension. The most of the methods are directly mapped to it.
 * Exceptions are "Information methods" and "Get WinDBG API interfaces".\n
 * To get it in script call \ref dbg() function.
 */
class WindbgExt
{
public:

	/// \name Information methods
	//@{
	virtual ULONG STDMETHODCALLTYPE GetVersion() = 0; ///< Get extension version (high word - major, low word - minor)
	virtual const char* STDMETHODCALLTYPE GetVersionStr() = 0;	///< Get extension version string
	/// Return string with comma separated list of available WinDBG API interfaces
	virtual const char* STDMETHODCALLTYPE GetSupportedInterfaces() = 0;
	/// Take list of WinDBG API interfaces required by script and return true if all are available and false otherwise
	virtual BOOL STDMETHODCALLTYPE CheckAvailableInterfaces(const char* ifaceList) = 0;
	virtual const NoThrowException* STDMETHODCALLTYPE RequireInterfaces(const char* ifaceList) = 0;	///< stop script execution if these WinDBG API interfaces aren't available
	//@}

	/** \name Get WinDBG API interfaces
	 * Returned interfaces can be lower (i.e. IDebugClient4 instead of IDebugClient7) because older WinDBG doesn't support them.
	 * Check its availability before use via WindbgExt::GetSupportedInterfaces, WindbgExt::CheckAvailableInterfaces or WindbgExt::RequireInterfaces.
	 */
	//@{
	virtual IDebugAdvanced3* STDMETHODCALLTYPE ExtAdvanced() = 0;
	virtual IDebugClient7* STDMETHODCALLTYPE ExtClient() = 0;
	virtual IDebugControl7* STDMETHODCALLTYPE ExtControl() = 0;
	virtual IDebugDataSpaces4* STDMETHODCALLTYPE ExtData() = 0;
	virtual IDebugRegisters2* STDMETHODCALLTYPE ExtRegisters() = 0;
	virtual IDebugSymbols5* STDMETHODCALLTYPE ExtSymbols() = 0;
	virtual IDebugSystemObjects4* STDMETHODCALLTYPE ExtSystem() = 0;

	/// \name Place for future interface getters
	/// Currently they will return NULL and stop script execution.
	//@{
	virtual void* STDMETHODCALLTYPE ExtDummy1() = 0;
	virtual void* STDMETHODCALLTYPE ExtDummy2() = 0;
	virtual void* STDMETHODCALLTYPE ExtDummy3() = 0;
	virtual void* STDMETHODCALLTYPE ExtDummy4() = 0;
	virtual void* STDMETHODCALLTYPE ExtDummy5() = 0;
	virtual void* STDMETHODCALLTYPE ExtDummy6() = 0;
	virtual void* STDMETHODCALLTYPE ExtDummy7() = 0;
	virtual void* STDMETHODCALLTYPE ExtDummy8() = 0;
	virtual void* STDMETHODCALLTYPE ExtDummy9() = 0;
	//@}
	//@}

	/// \name ExtExtension wrapped methods/variables
	//@{

	/// \name Information about the session.
	//@{

	virtual ULONG STDMETHODCALLTYPE Get_OutputWidth() = 0;
	
	/// Actual processor type.
	virtual ULONG STDMETHODCALLTYPE Get_ActualMachine() = 0;

	virtual ULONG STDMETHODCALLTYPE Get_Machine() = 0;
	virtual ULONG STDMETHODCALLTYPE Get_PageSize() = 0;
	virtual ULONG STDMETHODCALLTYPE Get_PtrSize() = 0;
	virtual ULONG STDMETHODCALLTYPE Get_NumProcessors() = 0;
	virtual ULONG64 STDMETHODCALLTYPE Get_OffsetMask() = 0;
	//@}

	/// \name Queries about the current debuggee information available.
	//@{
	virtual ULONG STDMETHODCALLTYPE Get_DebuggeeClass() = 0;
	virtual ULONG STDMETHODCALLTYPE Get_DebuggeeQual() = 0;
	virtual ULONG STDMETHODCALLTYPE Get_DumpFormatFlags() = 0;

	virtual bool STDMETHODCALLTYPE Get_IsRemote() = 0;
	virtual bool STDMETHODCALLTYPE Get_OutCallbacksDmlAware() = 0;
	
	virtual bool STDMETHODCALLTYPE IsUserMode(void) = 0;
	virtual bool STDMETHODCALLTYPE IsKernelMode(void) = 0;
	virtual bool STDMETHODCALLTYPE IsLiveLocalUser(void) = 0;
	virtual bool STDMETHODCALLTYPE IsMachine32(ULONG Machine) = 0;
	virtual bool STDMETHODCALLTYPE IsCurMachine32(void) = 0;
	virtual bool STDMETHODCALLTYPE IsMachine64(ULONG Machine) = 0;
	virtual bool STDMETHODCALLTYPE IsCurMachine64(void) = 0;
	virtual bool STDMETHODCALLTYPE Is32On64(void) = 0;
	virtual bool STDMETHODCALLTYPE CanQueryVirtual(void) = 0;
	virtual bool STDMETHODCALLTYPE HasFullMemBasic(void) = 0;
	virtual bool STDMETHODCALLTYPE IsExtensionRemote(void) = 0;
	virtual bool STDMETHODCALLTYPE AreOutputCallbacksDmlAware(void) = 0;
	//@}

	/// \name Common mode checks which stop script execution on mismatches.
	//@{
	virtual const NoThrowException* STDMETHODCALLTYPE RequireUserMode(void) = 0;
	virtual const NoThrowException* STDMETHODCALLTYPE RequireKernelMode(void) = 0;
	//@}

	/// \name Input
	//@{
	virtual ULONG STDMETHODCALLTYPE Input(PSTR Str, ULONG StrSize) = 0;
	virtual ULONG STDMETHODCALLTYPE InputW(PWSTR Str, ULONG StrSize) = 0;
	//@}

	/// \name Output.
	/// Warn, Err and Verb are convenience routines for the warning, error and verbose cases.
	//@{
	virtual void STDMETHODCALLTYPE EnableDml(bool Enable) = 0;

	/// Defaults to DEBUG_OUTPUT_NORMAL, but can be overridden to produce different output.
	virtual ULONG STDMETHODCALLTYPE Get_OutMask() = 0;
	
	virtual void STDMETHODCALLTYPE Out(PCSTR Str) = 0;
	virtual void STDMETHODCALLTYPE Warn(PCSTR Str) = 0;
	virtual void STDMETHODCALLTYPE Err(PCSTR Str) = 0;
	virtual void STDMETHODCALLTYPE Verb(PCSTR Str) = 0;
	virtual void STDMETHODCALLTYPE OutW(PCWSTR Str) = 0;
	virtual void STDMETHODCALLTYPE WarnW(PCWSTR Str) = 0;
	virtual void STDMETHODCALLTYPE ErrW(PCWSTR Str) = 0;
	virtual void STDMETHODCALLTYPE VerbW(PCWSTR Str) = 0;

	virtual void STDMETHODCALLTYPE DmlCmdLink(PCSTR Text, PCSTR Cmd) = 0;
	virtual void STDMETHODCALLTYPE DmlCmdExec(PCSTR Text, PCSTR Cmd) = 0;

	virtual void STDMETHODCALLTYPE RefreshOutputCallbackFlags(void) = 0;
	//@}

	/// \name Set the return status for an extension call if a specific non-S_OK status needs to be returned.
	//@{
	virtual void STDMETHODCALLTYPE SetCallStatus(__in HRESULT Status) = 0;
	//@}

	/// \name Cached symbol info.
	/// The cache is automatically flushed when the backing symbol info changes.
	//@{
	virtual ULONG STDMETHODCALLTYPE GetCachedSymbolTypeId(__inout PULONG64 Cookie, __in PCSTR Symbol, __out PULONG64 ModBase) = 0;
	virtual ULONG STDMETHODCALLTYPE GetCachedFieldOffset(__inout PULONG64 Cookie, __in PCSTR Type, __in PCSTR Field, __out_opt PULONG64 ModBase = NULL, __out_opt PULONG TypeId = NULL) = 0;
	virtual bool STDMETHODCALLTYPE GetCachedSymbolInfo(__in ULONG64 Cookie, __out PDEBUG_CACHED_SYMBOL_INFO Info) = 0;
	virtual bool STDMETHODCALLTYPE AddCachedSymbolInfo(__in PDEBUG_CACHED_SYMBOL_INFO Info, __out PULONG64 Cookie) = 0;
	//@}

	/// \name Module information helpers.
	//@{
	virtual void STDMETHODCALLTYPE GetModuleImagehlpInfo(__in ULONG64 ModBase, __out struct _IMAGEHLP_MODULEW64* Info) = 0;
	virtual bool STDMETHODCALLTYPE ModuleHasGlobalSymbols(__in ULONG64 ModBase) = 0;
	virtual bool STDMETHODCALLTYPE ModuleHasTypeInfo(__in ULONG64 ModBase) = 0;
	//@}

	/// \name Expression evaluation
	// @{
	virtual PCSTR STDMETHODCALLTYPE GetExpr64(__in PCSTR Str, __in bool Signed, __in ULONG64 Limit, __out PULONG64 Val) = 0;
	virtual PCSTR STDMETHODCALLTYPE GetExprU64(__in PCSTR Str, __in ULONG64 Limit, __out PULONG64 Val) = 0;
	virtual PCSTR STDMETHODCALLTYPE GetExprS64(__in PCSTR Str, __in LONG64 Limit, __out PLONG64 Val) = 0;
	//@}

	/// \name Capture support
	//@{
	virtual CaptureOutputA* STDMETHODCALLTYPE CreateCaptureOutputA() = 0;
	virtual CaptureOutputW* STDMETHODCALLTYPE CreateCaptureOutputW() = 0;
	//@}

	//@}

};

